
/**
 * AppleIIGo
 * Keyboard class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <string.h>
#include <stdlib.h>
#include "Keyboard.h"

UINT32 keyboardLanguageMap[KEYBOARDLANGUAGE_END][10] = {
	// Default
	{'#', '@', '[', '\\', ']', '`', '{', '|', '}', '~'},
	// UK
	{UNICODE_POUND, '@', '[', '\\', ']', '`', '{', '|', '}', '~'},
	// French
	{UNICODE_POUND, UNICODE_AGRAVE, UNICODE_DEGREE, UNICODE_CCEDILLA,
		UNICODE_SECTION, '`', UNICODE_EACUTE, UNICODE_UGRAVE, UNICODE_EGRAVE,
		UNICODE_DIAERESIS},
	// Danish
	{'#', '@', UNICODE_CAPITALAE, UNICODE_CAPITALOSTROKE, UNICODE_CAPITALARING,
		'`', UNICODE_AE, UNICODE_OSTROKE, UNICODE_ARING, '~'},
	// Spanish
	{UNICODE_POUND, UNICODE_SECTION, UNICODE_INVEXCLAMATION,
		UNICODE_CAPITALNTILDE, UNICODE_INVQUESTION, '`', '"', UNICODE_NTILDE,
		UNICODE_CCEDILLA, '~'},
	// Italian
	{UNICODE_POUND, UNICODE_SECTION, '"', UNICODE_CCEDILLA, UNICODE_EACUTE,
		UNICODE_UGRAVE, UNICODE_AGRAVE, UNICODE_OGRAVE, UNICODE_EGRAVE,
		UNICODE_IGRAVE},
	// German
	{'#', UNICODE_SECTION, UNICODE_CAPITALADIAERESIS,
		UNICODE_CAPITALODIAERESIS, UNICODE_CAPITALUDIAERESIS, '`',
		UNICODE_ADIAERESIS, UNICODE_ODIAERESIS, UNICODE_UDIAERESIS,
		UNICODE_SHARPS},
	// Swedish
	{'#', '@', UNICODE_CAPITALADIAERESIS, UNICODE_CAPITALODIAERESIS,
		UNICODE_CAPITALARING, '`', UNICODE_ADIAERESIS, UNICODE_ODIAERESIS,
		UNICODE_ARING, '~'},
};

Keyboard::Keyboard(Motherboard * motherboard, bool isLowercase) : 
Device(motherboard, "keyboard") {
	initKeyLatch(isLowercase);
	initKeyModifier();
	initPasteBuffer();
	initIIc40Switch();
	
	setLanguage(KEYBOARDLANGUAGE_DEFAULT);
}

void Keyboard::onDevicesConnected() {
	paddles = motherboard->getPaddles();
	adb = motherboard->getAdb();
}

void Keyboard::onResetAsserted(bool isRestart) {
	clearPasteBuffer();
}

void Keyboard::initKeyLatch(bool isLowercase) {
	this->isLowercase = isLowercase;
	keyLatch = 0;
	isAnyKeyDownValue = false;
}

void Keyboard::setKeyLatch(UINT32 unicode) {
	UINT8 key = 0;
	
	if (unicode < 128) {
		key = unicode;
		
		if (!isLowercase && (key >= 'a') && (key <= 'z'))
			key -= 0x20;
	} else {
		for (UINT32 index = 0; index < 10; index++)
			if (keyboardLanguageMap[languageIndex][index] == unicode)
				key = keyboardLanguageMap[KEYBOARDLANGUAGE_DEFAULT][index];
	}
	
	if (key)
		keyLatch = maskWithBit7(key, true);
}

UINT8 Keyboard::getKeyLatch() {
	return keyLatch;
}

void Keyboard::clearKeyLatch() {
	keyLatch = maskWithBit7(keyLatch, false);
	
	if (pasteBuffer)
		pasteNextKey();
}

bool Keyboard::isKeyLatchClear() {
	return !(keyLatch & 0x80);
}

void Keyboard::pushKey(UINT32 unicode) {
	if (!pasteBuffer)
		setKeyLatch(unicode);
}

void Keyboard::setAnyKeyDown(bool value) {
	isAnyKeyDownValue = value;
}

bool Keyboard::isAnyKeyDown() {
	return isAnyKeyDownValue;
}

void Keyboard::initKeyModifier() {
	for (int index = 0; index < KEYMODIFIER_END; index++)
		keyModifierCount[index] = 0;
}

void Keyboard::updateAdbKeyModifier() {
	UINT8 adbKeyModifier = 0;
	setBit(adbKeyModifier, ADBKEYMODIFIER_SHIFT,
		   keyModifierCount[KEYMODIFIER_SHIFT]);
	setBit(adbKeyModifier, ADBKEYMODIFIER_CONTROL,
		   keyModifierCount[KEYMODIFIER_CONTROL]);
	setBit(adbKeyModifier, ADBKEYMODIFIER_CAPSLOCK,
		   keyModifierCount[KEYMODIFIER_CAPSLOCK]);
	setBit(adbKeyModifier, ADBKEYMODIFIER_REPEAT, 0);
	setBit(adbKeyModifier, ADBKEYMODIFIER_KEYPAD,
		   keyModifierCount[KEYMODIFIER_KEYPAD]);
	setBit(adbKeyModifier, ADBKEYMODIFIER_OPTION,
		   keyModifierCount[KEYMODIFIER_OPTION]);
	setBit(adbKeyModifier, ADBKEYMODIFIER_COMMAND,
		   keyModifierCount[KEYMODIFIER_COMMAND]);
	adb->setKeyModifierRegister(adbKeyModifier);
}

void Keyboard::pushKeyModifier(UINT32 index) {
	if (index < KEYMODIFIER_END) {
		keyModifierCount[index]++;
		
		if (index == KEYMODIFIER_COMMAND)
			paddles->pushButton(0);
		if (index == KEYMODIFIER_OPTION)
			paddles->pushButton(1);
		if (index == KEYMODIFIER_SHIFT)
			paddles->pushButton(2);
		
		updateAdbKeyModifier();
	}
}

void Keyboard::pullKeyModifier(UINT32 index) {
	if (index < KEYMODIFIER_END) {
		keyModifierCount[index]--;
		
		if (index == KEYMODIFIER_COMMAND)
			paddles->pullButton(0);
		if (index == KEYMODIFIER_OPTION)
			paddles->pullButton(1);
		if (index == KEYMODIFIER_SHIFT)
			paddles->pullButton(2);
		
		updateAdbKeyModifier();
	}
}

bool Keyboard::isKeyModifierDown(UINT32 index) {
	return keyModifierCount[index];
}

void Keyboard::initPasteBuffer() {
	pasteBuffer = 0;
}

void Keyboard::clearPasteBuffer() {
	if (pasteBuffer)
		free(pasteBuffer);
	pasteBuffer = 0;
}

UINT8 Keyboard::getPasteBufferByte() {
	if (!pasteBuffer)
		return 0;
	
	UINT32 value = pasteBuffer[pasteBufferCharIndex];
	pasteBufferCharIndex++;
	if (pasteBufferCharIndex == pasteBufferCharNum)
		clearPasteBuffer();
	return value;
}

void Keyboard::pasteNextKey() {
	UINT32 unicode;
	
	while ((unicode = getPasteBufferByte()) & 0x80);
	if (unicode)
		setKeyLatch(unicode);
}

void Keyboard::paste(char * buffer) {
	clearPasteBuffer();
	pasteBuffer = strdup(buffer);
	pasteBufferCharIndex = 0;
	pasteBufferCharNum = strlen(buffer);
	
	if (isKeyLatchClear())
		pasteNextKey();
}

void Keyboard::setLanguage(UINT32 value) {
	languageIndex = value;
}

UINT32 Keyboard::getLanguage() {
	return languageIndex;
}

void Keyboard::initIIc40Switch() {
	isIIc40SwitchValue = false;
}

void Keyboard::toggleIIc40Switch() {
	isIIc40SwitchValue = !isIIc40SwitchValue;
}

bool Keyboard::isIIc40Switch() {
	return isIIc40SwitchValue;
}
